package cn.jolyoulu.adminweb.service.impl;


import cn.jolyoulu.adminweb.entity.vo.SysRoleVO;
import cn.jolyoulu.adminweb.entity.vo.SysUserInfoVO;
import cn.jolyoulu.adminweb.utils.AuthUtils;
import cn.jolyoulu.common.core.utils.encryption.MD5Utils;
import cn.jolyoulu.common.web.security.entity.LoginUser;
import cn.jolyoulu.common.web.security.entity.TokenInfo;
import cn.jolyoulu.common.web.security.service.TokenService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import cn.jolyoulu.cmnservice.constant.ServiceRedisConstant;
import cn.jolyoulu.adminweb.entity.bo.LoginBO;
import cn.jolyoulu.adminweb.service.ApiSysUserAuthService;
import cn.jolyoulu.cmnservice.entity.po.HoldUser;
import cn.jolyoulu.cmnservice.entity.po.SysRole;
import cn.jolyoulu.cmnservice.entity.po.SysUser;
import cn.jolyoulu.cmnservice.serivce.SysMenuService;
import cn.jolyoulu.cmnservice.serivce.SysRoleService;
import cn.jolyoulu.cmnservice.serivce.SysUserService;
import cn.jolyoulu.cmnservice.utils.HoldUserUtils;
import cn.jolyoulu.common.redis.utils.RedisUtils;
import cn.jolyoulu.common.web.enums.GlobalExpType;
import cn.jolyoulu.common.web.excption.GlobalException;

import java.nio.charset.StandardCharsets;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author LuZhouJin
 * @Date 2023/3/16
 */
@Slf4j
@Service
public class ApiSysUserAuthServiceImpl implements ApiSysUserAuthService {

    @Autowired
    private RedisUtils redisUtils;

    @Autowired
    private SysUserService sysUserService;

    @Autowired
    private ApiSysUserAuthService apiSysUserAuthService;

    @Autowired
    private SysMenuService sysMenuService;

    @Autowired
    private SysRoleService sysRoleService;

    @Autowired
    private AuthUtils authUtils;

    @Autowired
    private TokenService tokenService;

    @Value("${spring.profiles.active}")
    private String active;


    @Override
    public String login(LoginBO bo) {
        //校验验证码 (开发环境通过验证码)
        if (!active.equals("dev")){
            apiSysUserAuthService.checkVerifyCode(bo);
        }
        //账号密码校验
        SysUser user = apiSysUserAuthService.checkPwd(bo);
        LoginUser<SysUser> loginUser = new LoginUser<SysUser>();
        loginUser.setUser(user);
        TokenInfo tokenInfo = tokenService.createToken(loginUser);
        //生成token，并且保存到redis中
        String token = authUtils.genToken(user.getId());
        return tokenInfo.getAccessToken();
    }

    @Override
    public SysUserInfoVO userInfo() {
        String userId = HoldUserUtils.get().getUserId();
        SysUser user = sysUserService.getById(userId);
        //角色信息
        List<SysRole> sysRoles = sysRoleService.selectByUserId(userId);
        List<SysRoleVO> roleVOS = sysRoles.stream().map(SysRoleVO::new).collect(Collectors.toList());
        //加载权限数据
        Set<String> permissionMenu = apiSysUserAuthService.selectPermissionMenu(sysRoles);
        return new SysUserInfoVO(user, permissionMenu, roleVOS);
    }

    @Override
    public void checkVerifyCode(LoginBO bo) {
        String key = ServiceRedisConstant.getVerifycodeimg(bo.getVerifyCodeId());
        String code = redisUtils.get(key);
        if (Objects.isNull(code)) {
            throw new GlobalException(GlobalExpType.NOT_CODE);
        }
        if (!code.equalsIgnoreCase(bo.getVerifyCode())){
            throw new GlobalException(GlobalExpType.CODE_ERROR);
        }
        //通过后删除验证码
        redisUtils.del(key);
    }

    @Override
    public SysUser checkPwd(LoginBO bo) {
        SysUser dbUser = sysUserService.getByAccount(bo.getAccount());
        if (Objects.isNull(dbUser)) {
            throw new GlobalException(GlobalExpType.USER_ACCOUNT_ERROR);
        }
        if (dbUser.getStatus().equals(2)) {
            throw new GlobalException(GlobalExpType.USER_ACCOUNT_FORBIDDEN);
        }
        //使用base64对密码解密
        byte[] decodePwd = Base64.getDecoder().decode(bo.getPassword());
        String formPwd = new String(decodePwd, StandardCharsets.UTF_8);
        //使用盐加密表单代码与数据库比对
        String md5FormPwd = MD5Utils.encode(formPwd + dbUser.getSalt());
        String md5DbPwd = dbUser.getPassword();
        if (!md5FormPwd.equals(md5DbPwd)) {
            throw new GlobalException(GlobalExpType.USER_ACCOUNT_ERROR);
        }
        return dbUser;
    }

    @Override
    public void checkUserStatus(String userId) {
        SysUser dbUser = sysUserService.getById(userId);
        if (Objects.isNull(dbUser)) {
            throw new GlobalException(GlobalExpType.TOKEN_NO_AVAIL);
        }
        if (dbUser.getStatus().equals(2)) {
            throw new GlobalException(GlobalExpType.USER_ACCOUNT_FORBIDDEN);
        }
    }

    @Override
    public HoldUser getHoldUser(String userId) {
        //用户信息
        SysUser user = sysUserService.getById(userId);
        //角色信息
        List<SysRole> sysRoles = sysRoleService.selectByUserId(userId);
        //权限信息
        Set<String> permissionMenu = apiSysUserAuthService.selectPermissionMenu(sysRoles);
        return new HoldUser()
                .setSysUser(user)
                .setPermissions(permissionMenu)
                .setRoles(sysRoles.stream().map(HoldUser.Role::new).collect(Collectors.toList()));
    }

    @Override
    public Set<String> selectPermissionMenu(List<SysRole> sysRoles) {
        Set<String> res = new HashSet<>();
        if (Objects.isNull(sysRoles) || sysRoles.isEmpty()) {
            return res;
        }
        sysRoles.forEach(item -> {
            res.addAll(sysMenuService.selectPermissionMenuByRoleId(item.getId()));
        });
        return res;
    }

    @Override
    public void logOut() {
        HoldUser holdUser = HoldUserUtils.get();
        String key = ServiceRedisConstant.getToken(holdUser.getUserId());
        redisUtils.del(key);
    }

    @Override
    public void checkToken(String handlerToken, String userId) {
        String key = ServiceRedisConstant.getToken(userId);
        String redisToken = redisUtils.get(key);
        if (!handlerToken.equals(redisToken)) {
            throw new GlobalException(GlobalExpType.UNAUTHORIZED);
        }
    }
}
